#include "w25q32.h"

/* 初始化SPI要用到的片选口 */
const GPIO_Init_Param_Typedef GPIO_FLASH[2] = {
    {GPIOA, {LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 1}, // FLASH 片选
    {0, {0, 0, 0, 0, 0}, 0}};

/* 4K缓冲区 */
uint8_t sector_data[W25Q32_SECTOR_SIZE];

void flash_init()
{
    GPIO_Init((GPIO_Init_Param_Typedef *)GPIO_FLASH);
}

uint16_t flash_get_id(void)
{
    uint16_t id = 0;
    // 发送读制造/器件号指令 0x90
    FLASH_CS = 0;
    flash_byte_write(0x90);
    // 发送24位地址
    flash_byte_write(0x00);
    flash_byte_write(0x00);
    flash_byte_write(0x00);
    id = flash_byte_read() << 8; // 生产ID
    id |= flash_byte_read();     // 器件ID
    FLASH_CS = 1;
    return id;
}

// 写使能
void flash_write_enable(void)
{
    FLASH_CS = 0;
    flash_byte_write(0x06); // 写使能
    FLASH_CS = 1;
}

// 读取状态寄存器1
uint8_t flash_read_state1(void)
{
    uint8_t state = 0;
    FLASH_CS = 0;
    flash_byte_write(0x05);
    state = flash_byte_read(); // 读取状态值
    FLASH_CS = 1;
    return state;
}

// 读取状态寄存器2
uint8_t flash_read_state2(void)
{
    uint8_t state = 0;
    FLASH_CS = 0;
    flash_byte_write(0x35);
    state = flash_byte_read(); // 读取状态值
    FLASH_CS = 1;
    return state;
}
// 读取状态寄存器3
uint8_t flash_read_state3(void)
{
    uint8_t state = 0;
    FLASH_CS = 0;
    flash_byte_write(0x15);
    state = flash_byte_read(); // 读取状态值
    FLASH_CS = 1;
    return state;
}

void flash_sector_erace(uint32_t addr)
{
    while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    FLASH_CS = 0;
    flash_byte_write(0x20);                  // 扇区擦除
    flash_byte_write((uint8_t)(addr >> 16)); // 发送24位地址
    flash_byte_write((uint8_t)(addr >> 8));
    flash_byte_write((uint8_t)(addr));
    FLASH_CS = 1;
}

void flash_block_erase(uint32_t addr)
{
    FLASH_CS = 0;
    flash_byte_write(0x06);                  // 写使能
    flash_byte_write(0xDB);                  // 扇区擦除
    flash_byte_write((uint8_t)(addr >> 16)); // 发送24位地址
    flash_byte_write((uint8_t)(addr >> 8));
    flash_byte_write((uint8_t)(addr));
    FLASH_CS = 1;
}

void flash_read_data(uint32_t addr, uint8_t *data, uint16_t size)
{
    // 如果地址就是最大地址,那应该还是可以读一个字节的,
    if (addr + size - 1 > W25Q32_END_ADDR)
    {
        return;
    }
    uint8_t *pData = data;
    while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
    {
    }
    FLASH_CS = 0;
    flash_byte_write(0x03);       // 读数据指令
    flash_byte_write(addr >> 16); // 发送24位地址
    flash_byte_write(addr >> 8);
    flash_byte_write(addr);
    while (size--)
    {
        *pData = flash_byte_read(); // 保存数据
        pData++;
    }
    FLASH_CS = 1;
}

// 页编程,这个是直接写,基本不做任何校验
void flash_page_write(uint32_t addr, uint8_t *data, uint16_t size)
{
    if (addr + size - 1 > W25Q32_END_ADDR)
        return;
    uint8_t *pData = data;
    while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    while (!(flash_read_state1() & W25Q32_WEL))
    { // 等待写使能完成
    }
    FLASH_CS = 0;
    flash_byte_write(0x02);       // 页写指令
    flash_byte_write(addr >> 16); // 发送24位地址
    flash_byte_write(addr >> 8);
    flash_byte_write(addr);
    while (size--)
    {
        flash_byte_write(*pData);
        pData++;
    }
    FLASH_CS = 1;
}

// 可以写超过一页的数据,会自动分页写,也没有做任何校验
void flash_stepover_pagewrite(uint32_t addr, uint8_t *data, uint32_t size)
{
    uint32_t addr_remain = 256 - addr % 256; // 参数给的addr之后,当前页地址剩余多少字节
    uint8_t *pData = data;
    if (size <= addr_remain) // 当前页就够了
    {
        addr_remain = size;
    }
    while (1)
    {
        flash_page_write(addr, pData, addr_remain);
        if (addr_remain == size)
        {
            break; // 数据全部写入
        }
        pData += addr_remain;    // buf数据地址偏移
        addr += addr_remain;     // flash地址偏移
        size -= addr_remain;     // size计算剩余数据
        addr_remain = 256;       // 写入一页数据
        if (size <= addr_remain) // 计算当前页是否够写入剩余数据
        {
            addr_remain = size;
        }
    }
}

// 这个可以自动擦除扇区,并写入
void flash_write_data(uint32_t addr, uint8_t *data, uint32_t size)
{
    uint16_t sector_offset = addr % 4096;          // 计算当前扇区的地址偏移
    uint16_t sector_remain = 4096 - sector_offset; // 计算当前扇区剩余
    uint32_t sector_addr = addr - sector_offset;   // 计算当前扇区的起始地址
    uint8_t *pData = data;
    uint32_t i;
    if (size <= sector_remain)
    {
        sector_remain = (uint16_t)size;
    }
    while (1)
    {
        flash_read_data(addr, sector_data, sector_remain); // 读取要写入地址的数据,看看是不是为全0xFF
        for (i = 0; i < sector_remain; i++)
        {
            if (sector_data[i] != 0xFF)
                break;
        }
        if (i != sector_remain) // 如果不是就要擦除扇区,判断是否需要擦除扇区
        {
            // 擦除前保存当前扇区前一段数据
            flash_read_data(sector_addr, sector_data, sector_offset);
            // 擦除前保存当前扇区后一段数据
            flash_read_data(addr + sector_remain,
                            sector_data + (sector_offset + sector_remain),
                            W25Q32_SECTOR_SIZE - (sector_offset + sector_remain));
            flash_sector_erace(sector_addr); // 擦除扇区
            // 将要写入的数据插入缓冲区
            for (i = 0; i < sector_remain; i++)
            {
                sector_data[sector_offset + i] = pData[i];
            }
            flash_stepover_pagewrite(sector_addr, sector_data, W25Q32_SECTOR_SIZE);
            sector_offset = 0;
        }
        else
        {
            flash_stepover_pagewrite(addr, pData, sector_remain); // 向当前扇区写入数据
        }
        if (sector_remain == size)
            break; // 全部数据完全写入

        pData += sector_remain;             // 数据地址偏移
        addr += sector_remain;              // flash地址偏移
        sector_addr = addr;                 // 当前扇区起始地址
        size -= sector_remain;              // 数据量减少
        sector_remain = W25Q32_SECTOR_SIZE; // 当前扇区剩余
        if (size <= W25Q32_SECTOR_SIZE)     // 计算当前扇区是否够写入剩余数据
        {
            sector_remain = size;
        }
    }
}

void Transmit_1byte(uint8_t d)
{
    while (!LL_USART_IsActiveFlag_TXE(USART1))
    {
    }
    LL_USART_TransmitData8(USART1, d);
}

void flash_erace_chip()
{
    while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    FLASH_CS = 0;
    flash_byte_write(0xC7); // 芯片擦除
    FLASH_CS = 1;
    while (flash_read_state1() & W25Q32_BUSY) // 等待忙结束
    {
        Transmit_1byte(TASK_BUSY);
        LL_mDelay(100);
    }
}

uint8_t zk_is_set_ok()
{
    uint16_t tmp = 0;
    flash_read_data(ADDR_ZK_OK, (uint8_t *)&tmp, 2);
    return (*(uint16_t *)&tmp == 0x4B4F);
}

uint8_t clear_zk_ok_flag()
{
    uint16_t tmp = 0;
    flash_write_data(ADDR_ZK_OK, (uint8_t *)zk_not_ok_flag, 2);
    flash_read_data(ADDR_ZK_OK, (uint8_t *)&tmp, 2);
    return (*(uint16_t *)&tmp == 0xFFFF);
}

void flash_program()
{
    uint32_t addr, size, cmd, count;
    // 初始化串口
    LL_TIM_DisableCounter(TIM1);
    USART1_Init_Without_Interrupt(FLASH_BUAD);
    // 循环索取命令和数据
    while (1)
    {
        cmd = 0;
        Transmit_1byte(START_RECEIVE_TASK);
        USART1_read_data((uint8_t *)&cmd, 4, 100);
        switch (cmd)
        {
        case GET_FLASH_ID:
            Transmit_1byte(START_SEND_DATA);
            Transmit_1byte(flash_get_id());
            Transmit_1byte(flash_get_id() >> 8);
            Transmit_1byte(TASK_FINISH);
            break;
        case ERASE_CHIP:
            flash_erace_chip();
            Transmit_1byte(TASK_FINISH);
            break;

        case PROGRAM:
            // 读地址
            Transmit_1byte(READ_PRAMA);
            USART1_read_data((uint8_t *)&addr, 4, 20);
            // 读个数
            Transmit_1byte(READ_PRAMA);
            USART1_read_data((uint8_t *)&size, 4, 20);
            // 读数据
            Transmit_1byte(READ_PRAMA);
            count = USART1_read_data(sector_data, size, 700);
            // 写入flash
            if (count == size)
            {
                // 反馈任务完成
                flash_stepover_pagewrite(addr, sector_data, size);
                Transmit_1byte(TASK_FINISH);
            }
            break;

        case READDATA:
            // 读地址
            Transmit_1byte(READ_PRAMA);
            USART1_read_data((uint8_t *)&addr, 4, 20);
            // 读个数
            Transmit_1byte(READ_PRAMA);
            USART1_read_data((uint8_t *)&size, 4, 20);
            // 反馈
            if (addr + size > W25Q32_END_ADDR + 1)
            {
                Transmit_1byte(ERROR);
                break;
            }
            Transmit_1byte(START_SEND_DATA);
            // 等待忙结束
            while (flash_read_state1() & W25Q32_BUSY)
            {
            }
            FLASH_CS = 0;
            flash_byte_write(0x03);       // 读数据指令
            flash_byte_write(addr >> 16); // 发送24位地址
            flash_byte_write(addr >> 8);
            flash_byte_write(addr);
            while (size--)
            {
                Transmit_1byte(flash_byte_read());
            }
            FLASH_CS = 1;
            break;

        case PROGRAM_EXIT:
            Transmit_1byte(TASK_FINISH);
            LL_TIM_EnableCounter(TIM1);
            return;

        case WRITE_ZK_OK_FLAG:
            flash_write_data(ADDR_ZK_OK, (uint8_t *)zk_ok_flag, 2);
            Transmit_1byte(zk_is_set_ok() ? TASK_FINISH : ERROR);
            break;

        default:
            break;
        }
    }
}